package threads

import database.ConnectionPool
import database.DatabaseAccountBuilder
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.Executors
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger


object Main {
    /**
     * 创建线程池
     */
    @JvmStatic
    fun buildThreadPool(size: Int = 10, threadName: String): ThreadPoolExecutor {
        val maxPoolSize = size * 2
        val keepAliveTime = 60L
        val workQueue = ArrayBlockingQueue<Runnable>(maxPoolSize)
        return ThreadPoolExecutor(size, maxPoolSize, keepAliveTime, TimeUnit.SECONDS, workQueue) { r ->
            // 这是用Lambda表达式设置线程的名字。再用Lambda表达式设置异常Handler，让Logger来打印问题
            Thread(r, threadName).apply {
                setUncaughtExceptionHandler { _, e ->
                    LogManager.getLogger(Main::class.java).error("发生了不可预料的异常", e)
                }
            }
        }
    }


    @JvmStatic
    fun main(args: Array<String>) {
        println(
            """寒棠Hantang Bilibili数据记录器
版　　本：3.0
制　　作：景育
编译日期：2023年5月2日
=========================================
"""
        )

        val logger: Logger = LogManager.getLogger(Main::class.java)
        logger.info("开始运行【寒棠】Bilibili数据记录器")

        logger.debug("开始创建线程池")
        val scheduledExecutor = Executors.newScheduledThreadPool(1) { r -> Thread(r, "sche") } // 该线程池用于每分钟发起任务
        val apiThreadPool = buildThreadPool(10,"api")  // 该线程池用于调用B站API
        val insertThreadPool = buildThreadPool(10, "inst")  // 该线程池用于把数据保存到数据库中

        logger.debug("开始读入数据库配置")
        val account = DatabaseAccountBuilder.build("config/database.json")
        logger.debug("开始创建数据库连接池")
        val connectionPool = ConnectionPool(account, 12) // 数据库连接池 理论上至少11个 10个插入 1个查询

        val queue = ArrayBlockingQueue<MediaListResponse>(20) // 该队列用于存放从API获取到的数据
        // 【消费者】把数据插入到数据库的线程
        for (i in 0 until 10) {
            insertThreadPool.execute(InsertThread(connectionPool, queue))
        }
        logger.debug("初始化数据插入线程完毕")

        // 【生产者】创建每分钟的任务，每分钟执行若干个apiThread的任务
        val minuteTask = MinuteTask(apiThreadPool, connectionPool, queue)
        scheduledExecutor.scheduleAtFixedRate(minuteTask, 0, 1, TimeUnit.MINUTES)
        logger.debug("初始化每分钟的任务完毕")

        // 注册钩子，让程序退出时也写一个日志
        Runtime.getRuntime().addShutdownHook(Thread( {
            logger.info("程序即将退出")
        }, "end "))
    }
}